package com.singerw.service.oss;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import com.singerw.pojo.File;
import com.singerw.service.TwOptionsService;
import com.singerw.utils.OSSUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @version V1.0
 * @Package com.singerw.service.oss
 * @auhter 张欣
 * @date 2021/1/3-7:09 PM
 */

@Service
public class OSSService {
    private final TwOptionsService twOptionsService;
    private String endpoint, accessKeyId, accessKeySecret, bucketName;
    public static final String basePath = "user_space/";
    private static OSS client = null;

    @Autowired
    public OSSService(TwOptionsService twOptionsService) {
        this.twOptionsService = twOptionsService;
        updateConfigure();
    }

    /**
     * 更新OSS配置并重新建立对象
     */
    public void updateConfigure() {
        JSONObject ossConfigureObject = JSON.parseObject(twOptionsService.findByName("oss_configure").getValue());
        endpoint = ossConfigureObject.getString("endpoint");
        accessKeyId = ossConfigureObject.getString("accessKeyId");
        accessKeySecret = ossConfigureObject.getString("accessKeySecret");
        bucketName = ossConfigureObject.getString("bucketName");
        client = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

    }

    /**
     * 上传文件
     *
     * @param path 目标路径
     * @param b    文件数据
     */
    public void putFile(String path, byte[] b) {
        if (client == null) {
            System.out.println("是空的");
            client = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        }
        client.putObject(bucketName, path, new ByteArrayInputStream(b));
    }

    /**
     * 获得文件列表
     *
     * @param path    目标文件路径
     * @param onlyDir 如果为真那么就只返回文件夹，反之返回全部
     */
    public String getFileList(String path, boolean onlyDir) {
        ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName);
        listObjectsRequest.setDelimiter("/");
        listObjectsRequest.setPrefix(path);

        ObjectListing listObjects = client.listObjects(listObjectsRequest);

        //getCommonPrefixes 是获取文件夹，而getObjectSummaries 是获取文件
        List<String> prefixes = listObjects.getCommonPrefixes();
        ArrayList<File> files = new ArrayList<>();
        for (String prefixe : prefixes) {
            if (prefixe.equals(path)) {
                continue;
            }
            files.add(new File(OSSUtils.removePrefix(prefixe), null, null));
        }
        if (!onlyDir) {
            List<OSSObjectSummary> Summaries = listObjects.getObjectSummaries();
            for (OSSObjectSummary Summary : Summaries) {
                if (Summary.getKey().equals(path)) {
                    continue;
                }
                String fileName = OSSUtils.removePrefix(Summary.getKey());
                if (fileName.equals("error")) {
                    continue;
                }
                files.add(new File(fileName, Summary.getSize(), Summary.getLastModified()));
            }
        }
        return JSON.toJSONString(files);
    }

    /**
     * 获得对应格式的文件列表
     *
     * @param userId 用户 ID
     * @param format 格式类型
     * @return
     */
    public String getFileFormatList(String userId, String format) {
        String[] typeArr = getFileTypeArr(format);
        List<OSSObjectSummary> sums = getAllFileList(userId,false);
        List<File> list = new ArrayList<>();
        for (OSSObjectSummary sum : sums) {
            String key = sum.getKey();
            for (String t :
                    typeArr) {
                String type = key.substring(key.length() - t.length(), key.length());
                if (type.contains(t)) {
                    list.add(new File(OSSUtils.removeBasePath(key), sum.getSize(), sum.getLastModified()));
                    break;
                }

            }


        }
        return JSON.toJSONString(list);
    }

    public String selectFile(String path, String name) {
        List<OSSObjectSummary> sums = getAllFileList(path,false);
        List<File> list = new ArrayList<>();
        for (OSSObjectSummary sum : sums) {
            String key = sum.getKey();
            String sub = key.substring(key.lastIndexOf("/"), key.length());
            if (sub.length() < 2) continue;
            if (sub.contains(name)) {
                list.add(new File(OSSUtils.removeBasePath(key), sum.getSize(), sum.getLastModified()));
            }
        }
        return JSON.toJSONString(list);


    }

    /**
     * 删除文件列表
     * <p>如果是文件，删除的同时会移动到用户的回收站目录</p>
     *
     * @param paths  待删除文件列表
     * @param userId 用户 ID
     */
    public void deleteFile(List<String> paths, String userId) {
        for (String path : paths) {
            if (!path.contains(userId)) {
                path = userId + path;
            }
            if (path.substring(path.length() - 1).equals("/")) {
                String nextMarker = null;
                ObjectListing objectListing = null;
                do {
                    ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName)
                            .withPrefix(path)
                            .withMarker(nextMarker);

                    objectListing = client.listObjects(listObjectsRequest);
                    if (objectListing.getObjectSummaries().size() > 0) {
                        List<String> keys = new ArrayList<>();
                        for (OSSObjectSummary s : objectListing.getObjectSummaries()) {
                            keys.add(s.getKey());
                        }
                        DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName).withKeys(keys);
                        client.deleteObjects(deleteObjectsRequest);
                    }

                    nextMarker = objectListing.getNextMarker();
                } while (objectListing.isTruncated());
            } else {
                System.out.println("要删除的文件： " + path);
//                moveRecycle(path, userId);
                client.deleteObject(bucketName, path);

            }


        }


    }

    public void deleteFile(String path, String userId) {
        List<String> list = new ArrayList<>();
        list.add(path);
        deleteFile(list, userId);
    }

    /**
     * 将文件复制到用户的回收站目录
     *
     * @param path
     * @param userId
     */
    private void moveRecycle(String path, String userId) {
        String name = path.substring(path.lastIndexOf("/") + 1, path.length());
        String recyclyPath = basePath + userId + "/回收站/" + name;
        System.out.println("删除的文件" + path);
        System.out.println("回收站" + recyclyPath);
        client.copyObject(bucketName, path, bucketName, recyclyPath);

    }

    public void moveFile(String userId, List<String> paths, String targetPath) {
        for (String path : paths) {
            if (path.lastIndexOf("/") == path.length() - 1) {
                for (OSSObjectSummary item : getAllFileList(userId + path, false)) {
                    String temp = item.getKey();

                    copyFile(item.getKey(), targetPath, path + OSSUtils.removePrefix(temp));
                }
            }else{
                copyFile(userId + path, targetPath);
                System.out.println(targetPath + "~~");
            }



            deleteFile(path, userId);

        }
    }

    public void moveFile(String userId, String path, String targetPath) {
        ArrayList<String> list = new ArrayList<>();
        list.add(path);
        moveFile(userId, list, targetPath);

    }


    /**
     * 新建文件夹
     *
     * @param path
     */
    public String mkDir(String path, String dirName) {
        byte[] bytes = new byte[0];
        client.putObject(bucketName, path + dirName + "/", new ByteArrayInputStream(bytes));
        return getFileList(path, false);
    }

    /**
     * 获取指定用户全部文件
     *
     * @param userId
     * @return
     */
    public List<OSSObjectSummary> getAllFileList(String userId,boolean hasDir) {

        final int maxKeys = 1000;
        ObjectListing objectListing = client.listObjects(
                new ListObjectsRequest(bucketName)
                        .withMaxKeys(maxKeys).withPrefix(userId));

        List<OSSObjectSummary> list = objectListing.getObjectSummaries();
        List<OSSObjectSummary> result = new ArrayList<>();
        for (OSSObjectSummary ossObjectSummary : list) {
            if(!hasDir){
                if (ossObjectSummary.getKey().equals(userId)) {
                    continue;
                }
            }

            result.add(ossObjectSummary);
        }
        return result;
    }
    public OSSObject getFile(String path){
        return client.getObject(bucketName, path);

    }
    /**
     * 获得对应空间的占用长度和文件数量
     *
     * @param path
     * @return
     */
    public JSONObject getSpaceInfo(String path) {
        List<OSSObjectSummary> allFileList = getAllFileList(path,false);
        long result = 0L;
        long number = 0L;
        for (OSSObjectSummary summary : allFileList) {
            long size = summary.getSize();
            result += size;
            if (size > 0) {
                number++;
            }
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("SpaceSize", result);
        jsonObject.put("FileNumber", number);
        return jsonObject;
    }

    public long getFileSize(String path){
        long result = 0l;
        if(path.indexOf(".") != -1){
            result = getFile(path).getObjectMetadata().getContentLength();
        }else{
            List<OSSObjectSummary> allFileList = getAllFileList(path,false);
            for (OSSObjectSummary ossObjectSummary : allFileList) {
                result += ossObjectSummary.getSize();
            }
        }
        return result;

    }

    /**
     * 下载文件
     * <p>返回文件下载的直链</p>
     *
     * @param path
     * @return
     */
    public String downloadFile(String path) {
        GeneratePresignedUrlRequest urlRequest = new GeneratePresignedUrlRequest(bucketName, path, HttpMethod.GET);
        Date expiration = new Date(new Date().getTime() + 3600 * 1000);
        urlRequest.setExpiration(expiration);
        String fileUrl = client.generatePresignedUrl(urlRequest).toString();
        String fileName = path.substring(path.lastIndexOf("/") + 1, path.length());
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("url", fileUrl);
        jsonObject.put("name", fileName);
        return jsonObject.toJSONString();

    }


    public void download(String path, HttpServletResponse response) throws IOException {
        OSSObject ossObject = client.getObject(bucketName, path);
        String fileName = OSSUtils.removePrefix(path);
        fileName = new String(fileName.getBytes(), "ISO8859-1");
        response.reset();
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
        response.setContentType("text/plain");
        response.setContentType("application/octet-stream; charset=utf-8");
        response.setHeader("Location", fileName);
        response.setHeader("Cache-Control", "max-age=0");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
        BufferedInputStream buff = new BufferedInputStream(ossObject.getObjectContent());
        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        byte[] car = new byte[1024];
        int l = 0;
        while (l < buff.available()) {
            int j = buff.read(car, 0, 1024);
            l += j;
            out.write(car, 0, j);
        }
        buff.close();
        out.close();

    }


    public void zipFilesDown(HttpServletRequest request, HttpServletResponse response, String name, String path) {
        try {
            String fileName = name + ".zip";
            // 创建临时文件
            java.io.File zipFile = java.io.File.createTempFile(name, ".zip");
            FileOutputStream f = new FileOutputStream(zipFile);
            CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
            // 用于将数据压缩成Zip文件格式
            ZipOutputStream zos = new ZipOutputStream(csum);

            List<OSSObjectSummary> allFileList = getAllFileList(path,false);

            // 遍历所有文件。
            for (OSSObjectSummary ossObjectSummary : allFileList) {
                String eachFileName = OSSUtils.removeCasualPath(path, ossObjectSummary.getKey());

                // 获取Object，返回结果为OSSObject对象
                OSSObject ossObject = client.getObject(bucketName, ossObjectSummary.getKey());
                // 读去Object内容  返回
                InputStream inputStream = ossObject.getObjectContent();
                // 对于每一个要被存放到压缩包的文件，都必须调用ZipOutputStream对象的putNextEntry()方法，确保压缩包里面文件不同名
                zos.putNextEntry(new ZipEntry(eachFileName));
                int bytesRead;
                // 向压缩文件中输出数据
                while ((bytesRead = inputStream.read()) != -1) {
                    zos.write(bytesRead);
                }
                inputStream.close();
                zos.closeEntry(); // 当前文件写完，定位为写入下一条项目
            }
            zos.close();
            String header = request.getHeader("User-Agent").toUpperCase();
            if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
                fileName = URLEncoder.encode(fileName, "utf-8");
                fileName = fileName.replace("+", "%20");    //IE下载文件名空格变+号问题
            } else {
                fileName = new String(fileName.getBytes(), "ISO8859-1");
            }
            response.reset();
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
            response.setContentType("text/plain");
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Location", fileName);
            response.setHeader("Cache-Control", "max-age=0");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

            FileInputStream fis = new FileInputStream(zipFile);
            BufferedInputStream buff = new BufferedInputStream(fis);
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            byte[] car = new byte[1024];
            int l = 0;
            System.out.println(zipFile.length());
            while (l < zipFile.length()) {
                int j = buff.read(car, 0, 1024);
                l += j;
                out.write(car, 0, j);
            }

            // 关闭流
            fis.close();
            buff.close();
            out.close();

            // 删除临时文件
            zipFile.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void reNameFile(String userId,String path,String name,String newName) {
        if(name.indexOf(".") != -1){
            client.copyObject(bucketName, path + name, bucketName, path + newName);
        }else{
            List<OSSObjectSummary> allFileList = getAllFileList(path + name, false);
            if(allFileList.size() == 0){
                mkDir(path,newName.substring(0,newName.length() - 1));
            }
            allFileList.forEach(item -> {
                String key = item.getKey();
                client.copyObject(bucketName, key, bucketName, key.replaceAll(path + name,path + newName));

            });
        }
        deleteFile(path + name, userId);

    }

    public void copyFile(String path, String newPath) {
        client.copyObject(bucketName, path, bucketName, newPath + OSSUtils.removePrefix(path));
    }

    public void copyFile(String path, String newPath, String newName) {

        client.copyObject(bucketName, path, bucketName, newPath + newName);
    }

    private String[] getFileTypeArr(String format) {
        switch (format) {
            case "img":
                return new String[]{"jpg", "jpeg", "png", "bmp", "gif"};
            case "music":
                return new String[]{"mp3", "wav", "aif"};
            case "video":
                return new String[]{"mp4", "avi", "wmv"};
            case "doc":
                return new String[]{"txt", "doc", "docx", "md"};

        }
        return new String[]{};

    }


    public void deleteFile(String path) {
        client.deleteObject(bucketName, path);
    }


}
